#include <boost/asio/detail/socket_ops.hpp>
#include <cstdint>
#include <iostream>
#include <boost/asio.hpp>
#include <json/json.h>
#include <json/value.h>
#include <json/writer.h>

const uint16_t HEAD_MSG_ID = sizeof(uint16_t);
const uint16_t HEAD_MSG_LEN = sizeof(uint16_t);
const uint16_t HEAD_TOTAL_LEN = HEAD_MSG_ID + HEAD_MSG_LEN;
const int max_length = 64;

int main(void)
{
    Json::Value value;
    value["id"] = 1001;
    value["data"] = "hello world";
    std::string send_msg = value.toStyledString();
    std::cout << "Send is: " << send_msg << std::endl;

    try
    {
        boost::asio::io_context io_context;
        boost::asio::ip::address address = boost::asio::ip::address::from_string("127.0.0.1");
        boost::asio::ip::tcp::endpoint endpoint(address, 12345);

        boost::asio::ip::tcp::socket socket(io_context);
        boost::system::error_code error = boost::asio::error::host_not_found;
        socket.connect(endpoint, error);
        if (error)
        {
            std::cerr << "Connect failed: " << error.message() << "\n";
            return -1;
        }

        for(int i = 0; i < 10; ++i)
        {
            uint16_t id = 1001;
            uint16_t length = send_msg.length();
            id = boost::asio::detail::socket_ops::host_to_network_short(id);
            uint16_t net_length = boost::asio::detail::socket_ops::host_to_network_short(length);
            char send_data[max_length] = { 0 };
            memcpy(send_data, &id, HEAD_MSG_ID);
            memcpy(send_data + HEAD_MSG_ID, &net_length, HEAD_MSG_LEN);
            memcpy(send_data + HEAD_TOTAL_LEN, send_msg.c_str(), length);
            boost::asio::write(socket, boost::asio::buffer(send_data, length + HEAD_TOTAL_LEN));

            char reply_head[HEAD_TOTAL_LEN];
            uint16_t reply_length = boost::asio::read(socket,boost::asio::buffer(reply_head, HEAD_TOTAL_LEN));
            uint16_t msgid = 0;
            uint16_t msglen = 0;
            memcpy(&msgid, reply_head, HEAD_MSG_ID);
            memcpy(&msglen, reply_head + HEAD_MSG_ID, HEAD_MSG_LEN);
            msgid = boost::asio::detail::socket_ops::network_to_host_short(msgid);
            msglen = boost::asio::detail::socket_ops::network_to_host_short(msglen);

            char msg[max_length] = { 0 };
            size_t  msg_length = boost::asio::read(socket,boost::asio::buffer(msg, msglen));
            
            std::cout << "Reply is: ";
            std::cout.write(msg, msglen) << std::endl;
            std::cout << "Reply id is " << msgid;
            std::cout << "Reply len is " << msglen;
            std::cout << "\n";
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
        return -2;
    }

    return 0;
}